因为毕设需要,准备搭建一个稍有规模的分布式cuckoo沙箱。目前进展:
在2个物理机上配置了cuckoo host,在1个虚拟机上搭建了distributed server。将这2个host挂载在了server端,向server端发送运行任务后,server端会安排2个host轮流完成运行任务。
cuckoo host
单个cuckoo沙箱节点是由以下部分组成的:
- cuckoo主程序×1
- virtualbox×1(里面安装了多个虚拟机系统,每个系统都配置好了agent)
- mysql数据库×1(用于存放当前cuckoo节点的运行记录)
- mongo数据库×1(用于存放当前cuckoo节点的运行报告,json格式)
单个cuckoo节点的功能:
- cuckoo -d(开启后台分析进程)
- cuckoo web(开启web端从而在网页上提交样本进行分析、查看报告)
- cuckoo api(开启api接口提交样本,如果后面要搭建distributed cuckoo也需要开启它)
distributed cuckoo
分布式cuckoo的组成:
- distributed server(运行着server的一个节点)
- distributed worker(多个cuckoo节点)
- 数据库×1(用于存放distributed server端下发任务的记录)
worker
组成分布式worker的cuckoo节点首先是一个独立可以运行沙箱任务的,已经配置好的节点。所以在将某个节点加入到分布式cuckoo之前,需要先测试一下它本身是否能顺利完成分析任务。
在此基础上,开启后台cuckoo(https://cuckoo.sh/docs/usage/start.html#cuckoo-in-the-background)
,开启api调用(cuckoo api)并注释掉其cuckoo.conf中的api_token = xxx(把这边注释掉就不需要api_token的验证了),并且额外运行:
supervisorctl start distributed
server
server节点的功能就是调度各个worker节点,本身不需要运行软件、生成报告。因此开启server的节点不要求运行cuckoo -d,只需要运行:
cuckoo distributed server
注册worker到server
curl http://server的ip:9003/api/node -F name=worker的名字 -F url=http://worker的ip:8090/
worker被server调用的方式就是通过api调用。当你向【server的ip:9003】下发任务时,server会寻找合适、空闲的worker,把任务下发到【worker的ip:8090】,下发任务到server时也可以直接指定让某个worker的某个虚拟机来运行本次的软件。
todo
以上都是基于cuckoo自带的distributed功能实现,总的来说能够完成分布式沙箱运行软件的目标。但是其本身有一些我认为待改进的地方:
worker本地的原始文件会被自动删除
将cuckoo host挂载到了distributed server后,当前cuckoo就成为了一个worker。server安排了某个软件运行task给worker执行结束后,worker会将运行报告以json格式存储到配置好的mongo数据库里。不会在本地存储pcap、截图等文件了(因为会在存储report到mongo之后立删除本地的这些文件,从而节省空间)。
然而本地存储的文件其实是很大的,比如不通过server下发任务,而是通过cuckoo host本地开启的web提交任务,结束后在.cuckoo/storage/analyses这个路径下会存储完整的pcap、截图、内存等文件,一般都有90MB。但存储到mongo数据库中的json其实是缩水了很多的,而且api(存储在calls表)和截图等(存储在fs.chunks)和报告(存储在 analysis)是分开存储的。worker之间的id会冲突
server下发任务之后会有一个distributed task id,worker收到server下发的程序后会产生自己的task id(这个id是每个worker节点自己从1开始累加标号的)。worker运行完毕后会把报告存到mongo数据库,对应的report有自己的mongoid。
然而report只和task id关联了,没有和distributed task id关联。
- 举一个report的例子(只截取了前面的一部分):1234567891011121314151617181920212223242526272829303132{"_id": ObjectId("5e12a0fc6696137e8ab1f46c"),"info": {"added": ISODate("2020-01-06T10:51:07.000Z"),"started": ISODate("2020-01-06T10:51:08.000Z"),"duration": 93,"analysis_path": "/home/tangmingyu/.cuckoo/storage/analyses/2","ended": ISODate("2020-01-06T10:52:41.000Z"),"owner": null,"score": 0,"id": 2,"category": "file","git": {"head": "13cbe0d9e457be3673304533043e992ead1ea9b2","fetch_head": "13cbe0d9e457be3673304533043e992ead1ea9b2"},"monitor": "2deb9ccd75d5a7a3fe05b2625b03a8639d6ee36b","package": "exe","route": "none","custom": null,"machine": {"status": "stopped","name": "cuckoo1","label": "cuckoo1","manager": "VirtualBox","started_on": "2020-01-06 10:51:08","shutdown_on": "2020-01-06 10:52:41"},"platform": "windows","version": "2.0.7","options": "procmemdump=yes,route=none"},
可以看出mongoid为5e12a0fc6696137e8ab1f46c,id为2.但这个id是task id,不是一个唯一对应值。比如有两个cuckoo节点,tmy和tmy2,它们各自运行任务时都会将task id从1开始标号,因此是有冲突的。
目前我的解决方法:
给不同的cuckoo节点安排不同的mongo数据库,比如给tmy这个节点安排的mongo数据库是cuckoo_db,给tmy安排的是cuckoo_db2,那么在每个db中id就可以唯一标志一个任务了。